package com.graviton;

public class JSONTokenizer {

    private CharReader charReader;

    private JSONTokens jsonTokens = new JSONTokens();

    public JSONTokens tokenize(CharReader chars) throws JsonParserException {
        charReader = chars;
        do {
            JSONToken jsonToken = tokenize(charReader.getChar());
            jsonTokens.add(jsonToken);
        } while (!charReader.isEnd());
        return jsonTokens;
    }

    private JSONToken tokenize(char c) throws JsonParserException {
        switch (c) {
            case '{':
                return new JSONToken(JSONTokenType.BEGIN_OBJECT, "{");
            case '}':
                return new JSONToken(JSONTokenType.END_OBJECT, "}");
            case '[':
                return new JSONToken(JSONTokenType.BEGIN_ARRAY, "[");
            case ']':
                return new JSONToken(JSONTokenType.END_ARRAY, "]");
            case ',':
                return new JSONToken(JSONTokenType.SEP_COMMA, ",");
            case ':':
                return new JSONToken(JSONTokenType.SEP_COLON, ":");
            case 'n':
                return readNull();
            case 't':
            case 'f':
                return readBoolean();
            case '"':
                return readString();
            case '-':
                return readNumber(c);
        }
        if (isDigist(c)) {
            return readNumber(c);
        }
        throw new JsonParserException("value error,post is " + charReader.getPosition());
    }

    private boolean isDigist(char c) {
        if (c >= '0' && c <= '9') {
            return true;
        }
        return false;
    }

    private JSONToken readNumber(char c) throws JsonParserException {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(c);
        for(;;){
            c = charReader.getChar();
            if (isDigist(c) || c == '.' || c == 'e' || c == 'E') {
                stringBuffer.append(c);
                if(!charIsNotEndToken(c) || charReader.isEnd()) {
                    return new JSONToken(JSONTokenType.NUMBER, stringBuffer.toString());
                }
            }else {
                throw new JsonParserException("number value error,post is " + charReader.getPosition());
            }
       }
    }

    private boolean charIsNotEndToken(char c) {
        return c != ':'  && c != ',' && c != ']' && c != '}' && c != '"';
    }

    private JSONToken readString() throws JsonParserException {
        StringBuffer stringBuffer = new StringBuffer();
        do {
            char c = charReader.getChar();
            if (c != '"') {
                stringBuffer.append(c);
            } else {
                return new JSONToken(JSONTokenType.STRING, stringBuffer.toString());
            }
        } while (!charReader.isEnd());
        throw new JsonParserException("string value error,post is " + charReader.getPosition());
    }

    private JSONToken readBoolean() throws JsonParserException {
        char c = charReader.getChar();
        if ('r' == c) {
            if ('u' == charReader.getChar() && 'e' == charReader.getChar()) {
                return new JSONToken(JSONTokenType.BOOLEAN, "true");
            }
        }

        if ('a' == c) {
            if ('l' == charReader.getChar() && 's' == charReader.getChar() && 'e' == charReader.getChar()) {
                return new JSONToken(JSONTokenType.BOOLEAN, "false");
            }
        }
        throw new JsonParserException("boolean value error,post is " + charReader.getPosition());
    }


    private JSONToken readNull() throws JsonParserException {
        if ('u' == charReader.getChar()
                && 'l' == charReader.getChar()
                && 'l' == charReader.getChar()) {
            return new JSONToken(JSONTokenType.NULL, "null");
        }
        throw new JsonParserException("null value error,post is " + charReader.getPosition());
    }
}
